<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="V0Wxj" id="V0Wxj"><span data-lake-id="ua7d659f2" id="ua7d659f2">典型回答</span></h1>
  <p data-lake-id="u1fb17027" id="u1fb17027"><span data-lake-id="ucac5fe2f" id="ucac5fe2f">ReentrantLock 和 synchronized 都是用于线程的同步控制，但它们在功能上来说差别还是很大的。对比下来 ReentrantLock 功能明显要丰富的多。</span></p>
  <p data-lake-id="u33cb1657" id="u33cb1657"><span data-lake-id="uefe3e659" id="uefe3e659">​</span><br></p>
  <p data-lake-id="ucafdff14" id="ucafdff14"><span data-lake-id="uebce9e70" id="uebce9e70">二者相同点是，都是可重入锁。二者也有很多不同，如：</span></p>
  <ul list="u4ef41952">
   <li fid="u79711a81" data-lake-id="u34312312" id="u34312312"><span data-lake-id="u86ccc617" id="u86ccc617">synchronized是Java内置特性，而ReentrantLock是通过Java代码实现的。</span></li>
   <li fid="u79711a81" data-lake-id="ud3862eed" id="ud3862eed"><strong><span data-lake-id="ub50e32ce" id="ub50e32ce">synchronized是可以自动获取/释放锁的</span></strong><span data-lake-id="u56cb0b53" id="u56cb0b53">，但是</span><strong><span data-lake-id="u08186c5d" id="u08186c5d">ReentrantLock需要手动获取/释放锁</span></strong><span data-lake-id="u3e7b563f" id="u3e7b563f">。</span></li>
   <li fid="u79711a81" data-lake-id="u473870c0" id="u473870c0"><span data-lake-id="ue2faf499" id="ue2faf499">ReentrantLock还具有响应中断、超时等待等特性。</span></li>
   <li fid="u79711a81" data-lake-id="ufafd74bf" id="ufafd74bf"><strong><span data-lake-id="uda4044e1" id="uda4044e1">ReentrantLock可以实现公平锁和非公平锁</span></strong><span data-lake-id="u7f507a67" id="u7f507a67">，而</span><strong><span data-lake-id="u67392634" id="u67392634">synchronized只是非公平锁</span></strong><span data-lake-id="u5d2f6abb" id="u5d2f6abb">。</span></li>
  </ul>
  <p data-lake-id="u7e48e4f5" id="u7e48e4f5"><span data-lake-id="u8f48d871" id="u8f48d871">​</span><br></p>
  <h1 data-lake-id="jjFZl" id="jjFZl"><span data-lake-id="uffeaedc7" id="uffeaedc7">扩展知识</span></h1>
  <p data-lake-id="uf94ffc71" id="uf94ffc71"><br></p>
  <h2 data-lake-id="ieLca" id="ieLca"><span data-lake-id="u94076e61" id="u94076e61">ReentrantLock用法</span></h2>
  <p data-lake-id="uee1ff5e3" id="uee1ff5e3"><br></p>
  <p data-lake-id="u435b5775" id="u435b5775"><span data-lake-id="u8e875b41" id="u8e875b41">Java语言直接提供了synchronized关键字用于加锁，但这种锁一是很重，二是获取时必须一直等待，没有额外的尝试机制。</span></p>
  <p data-lake-id="u6626e330" id="u6626e330"><span data-lake-id="u95f275d4" id="u95f275d4">​</span><br></p>
  <p data-lake-id="ua1849418" id="ua1849418"><span data-lake-id="u398d141a" id="u398d141a">java.util.concurrent.locks包提供的ReentrantLock用于替代synchronized加锁，ReentrantLock 内部是基于 AbstractQueuedSynchronizer（简称AQS）实现的。</span></p>
  <p data-lake-id="ua2de4500" id="ua2de4500"><span data-lake-id="ubb5932ef" id="ubb5932ef">​</span><br></p>
  <p data-lake-id="u7b329bf7" id="u7b329bf7"><span data-lake-id="u196bb551" id="u196bb551">ReentrantLock是可重入锁，它和synchronized一样，一个线程可以多次获取同一个锁。</span></p>
  <p data-lake-id="u809e20ac" id="u809e20ac"><span data-lake-id="u5a46463a" id="u5a46463a">​</span><br></p>
  <p data-lake-id="ue5247514" id="ue5247514"><span data-lake-id="u9209d96b" id="u9209d96b">用法：</span></p>
  <pre lang="java"><code>
public class Counter {
	private final Lock lock = new ReentrantLock();
	private int count;
	public void add(int n) {
		lock.lock();
		try {
			count += n;
		} finally {
			lock.unlock();
		}
	}
}
</code></pre>
  <p data-lake-id="uf7203b1d" id="uf7203b1d"><br></p>
  <h3 data-lake-id="NAEvH" id="NAEvH"><span data-lake-id="u8ac2a634" id="u8ac2a634" style="color: rgb(51, 51, 51)">怎么创建公平锁？</span></h3>
  <p data-lake-id="u538ee225" id="u538ee225"><br></p>
  <p data-lake-id="u905d5084" id="u905d5084"><span data-lake-id="ua954833d" id="ua954833d" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">new ReentrantLock() 默认创建的为非公平锁，如果要创建公平锁可以使用 new ReentrantLock(true)。</span></p>
  <h3 data-lake-id="C0eGQ" id="C0eGQ"><span data-lake-id="u6e5b7e49" id="u6e5b7e49" style="color: rgb(51, 51, 51)">lock() 和 lockInterruptibly() 的区别</span></h3>
  <p data-lake-id="u78a48714" id="u78a48714"><br></p>
  <p data-lake-id="u0d37de01" id="u0d37de01"><span data-lake-id="u7f8cdb64" id="u7f8cdb64" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">lock() 和 lockInterruptibly() 的区别在于获取锁的途中如果所在的线程中断，lock() 会忽略异常继续等待获取锁，而 lockInterruptibly() 则会抛出 InterruptedException 异常。</span></p>
  <p data-lake-id="u91dd23d6" id="u91dd23d6"><span data-lake-id="ubb983537" id="ubb983537" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">​</span><br></p>
  <h3 data-lake-id="OU3hu" id="OU3hu"><span data-lake-id="u9691a4d3" id="u9691a4d3" style="color: rgb(51, 51, 51)">tryLock() </span></h3>
  <p data-lake-id="ua47b3bf4" id="ua47b3bf4"><br></p>
  <p data-lake-id="u8d2ade27" id="u8d2ade27"><span data-lake-id="ua04b5494" id="ua04b5494" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">tryLock(5, TimeUnit.SECONDS) 表示获取锁的最大等待时间为 5 秒，期间会一直尝试获取，而不是等待 5 秒之后再去获取锁。</span></p>
  <p data-lake-id="u9be344e0" id="u9be344e0"><span data-lake-id="u196f85d3" id="u196f85d3" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">​</span><br></p>
  <h2 data-lake-id="a1l5I" id="a1l5I"><span data-lake-id="ud3619c9f" id="ud3619c9f">ReentrantLock 如何实现可重入的</span></h2>
  <p data-lake-id="u3fa56e05" id="u3fa56e05"><br></p>
  <p data-lake-id="u1715ef6a" id="u1715ef6a"><span data-lake-id="u970ad570" id="u970ad570">可重入锁指的是同一个线程中可以多次获取同一把锁。比如在JAVA中，当一个线程调用一个对象的加锁的方法后,还可以调用其他加同一把锁的方法，这就是可重入锁。</span></p>
  <p data-lake-id="u69fa1497" id="u69fa1497"><br></p>
  <p data-lake-id="u6a22554c" id="u6a22554c"><span data-lake-id="u1919e665" id="u1919e665">ReentrantLock 加锁的时候，看下当前持有锁的线程和当前请求的线程是否是同一个，一样就可重入了。 </span><span data-lake-id="u00345dc7" id="u00345dc7" class="lake-fontsize-12" style="color: rgb(77, 77, 77)">只需要简单得将state值加1，记录当前线程的重入次数即可。</span></p>
  <p data-lake-id="u84d95ffe" id="u84d95ffe"><br></p>
  <pre lang="java"><code>
if (current == getExclusiveOwnerThread()) {
     int nextc = c + acquires;
     if (nextc &lt; 0)
     	throw new Error("Maximum lock count exceeded");
     setState(nextc);
     return true;
 }
</code></pre>
  <p data-lake-id="uaba44473" id="uaba44473"><span data-lake-id="ua33a0dff" id="ua33a0dff">​</span><br></p>
  <p data-lake-id="u6d8aec4a" id="u6d8aec4a"><span data-lake-id="u0d02b85c" id="u0d02b85c">同时，在锁进行释放的时候，需要确保state=0的时候才能执行释放资源的动作，也就是说，一个可重入锁，重入了多少次，就得解锁多少次。</span></p>
  <p data-lake-id="uf1431be3" id="uf1431be3"><span data-lake-id="u0ac252d6" id="u0ac252d6">​</span><br></p>
  <pre lang="java"><code>
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
</code></pre>
 </body>
</html>